!function(global){// animator
    function Animator(ctx, width, height, painter){
	this.ctx = ctx;
	this.width = width;
	this.height = height;
	this.painter = painter;

	this.frameSkip = 0;
	this.__frame = 0;
	this.scale = 1;
	this.realTime = 0;
	this.time = 0;
	this.delta = 0;
	this.next = this.next.bind(this); // bind this to next

	// debug info 
	this.totalFrame = 0;
	this.fps = 0;
    }

    Animator.prototype.clear = function(){
	this.ctx.clearRect(0, 0, this.width, this.height);
    }

    Animator.prototype.paint = function(){
	this.clear();
	this.painter.paint(this);
    }

    Animator.prototype.update = function(){
	this.painter.update(this);
    }

    Animator.prototype.next = function(timestamp){
	this.delta += (timestamp - this.realTime) * this.scale;
	this.realTime = timestamp;
	if(this.__frame++ > this.frameSkip){
	    // debug
	    this.totalFrame += 1;
	    this.fps = 1000 / this.delta;

	    this.time += this.delta;
	    this.update();
	    this.paint();
	    this.__frame = 0;
	    this.delta = 0;
	}
	requestAnimationFrame(this.next);
    }

    Animator.prototype.animate = function(){
	requestAnimationFrame(this.next);
    }

    function PainterGroup(){
	this.painter = [];
    }

    PainterGroup.prototype.register = function(p){
	// fixme: need better method to determine if p.paint is a function
	if(!p || typeof p.paint == 'undefined') 
	    throw new Error('painter should have a paint function');
	// fixme: should an update function be optional
	if(!p || typeof p.update == 'undefined') 
	    throw new Error('painter should have a update function');
	this.painter.push(p);
    }

    PainterGroup.prototype.paint = function(animator){
	var painter = this.painter;
	var len = painter.length;
	for(var i = 0; i < len; i++)
	    painter[i].paint(animator);
    }

    PainterGroup.prototype.update = function(animator){
	var painter = this.painter;
	var len = painter.length;
	for(var i = 0; i < len; i++)
	    painter[i].update(animator);
    }

    // export to global
    global.Animator = Animator;
    global.PainterGroup = PainterGroup;
}(this);
